package cn.stimd.spring.beans.factory.support;

import cn.stimd.spring.beans.factory.annotation.Autowired;
import cn.stimd.spring.beans.factory.annotation.Qualifier;
import cn.stimd.spring.beans.factory.annotation.Value;
import cn.stimd.spring.beans.factory.config.BeanDefinitionHolder;
import cn.stimd.spring.beans.factory.config.DependencyDescriptor;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class QualifierAnnotationAutowireCandidateResolver implements AutowireCandidateResolver {
    private Class<? extends Annotation> qualifierType = Qualifier.class;
    private Class<? extends Annotation> valueAnnotationType = Value.class;


    @Override
    public boolean isAutowireCandidate(BeanDefinitionHolder holder, DependencyDescriptor descriptor) {
        boolean match = false;
        if(descriptor != null){
            match = checkQualifiers(holder, descriptor.getAnnotations());
        }
        return match;
    }


    /**
     * 依赖项上如果声明了具有Qualifier语义的注解，那么候选项也必须有对应的注解。
     * 这种注解分为两种：一种是单一注解（@Qualifier本身），另一种是复合注解（使用@Qualifier本身作为元注解）
     * 所谓对应是指，单一注解对单一注解，复合注解对复合注解
     *
     * 1) 依赖项是@LoadBalanced，候选项也是@LoadBalanced，匹配
     * 2) 依赖项是@LoadBalanced，候选项是@Qualifier，不匹配
     * 3) 依赖项是@Qualifier，候选项是@LoadBalanced，不匹配
     * 4) 依赖项是@Qualifier，候选项是@Qualifier，不匹配
     *
     * @param bdHolder              候选Bean相关信息
     * @param annotationsToSearch   依赖项上声明的注解
     */
    protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
        //依赖项没有声明注解，则默认是匹配的
        if (ObjectUtils.isEmpty(annotationsToSearch)) {
            return true;
        }

        for (Annotation annotation : annotationsToSearch) {
            Class<? extends Annotation> type = annotation.annotationType();
            boolean checkMeta = true;
            boolean fallbackToMeta = false;

            //1. 依赖项存在@Qualifier注解，且候选项声明了同样的注解
            //1.1 检查依赖项是否存在@Qualifier注解(单一注解或复合注解)
            if (isQualifier(type)) {
                //1.2 候选项没有声明同样的注解
                if (!checkQualifier(bdHolder, annotation)) {
                    fallbackToMeta = true;
                }
                //候选Bean也声明了同样的注解，不需要检查依赖项注解的元注解了
                else {
                    checkMeta = false;
                }
            }

            //2. 依赖项是一个复合注解，进一步检查其元注解
            if (checkMeta) {
                boolean foundMeta = false;
                for (Annotation metaAnn : type.getAnnotations()) {
                    Class<? extends Annotation> metaType = metaAnn.annotationType();
                    //2.1 检查复合注解的元注解是否为@Qualifier
                    if (isQualifier(metaType)) {
                        foundMeta = true;

                        //进一步检查@Qualifier注解的value属性
                        if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
                                !checkQualifier(bdHolder, metaAnn)) {
                            return false;
                        }
                    }
                }

                if (fallbackToMeta && !foundMeta) {
                    return false;
                }
            }
        }
        return true;
    }


    //检查指定的Bean是否为候选项
    protected boolean checkQualifier(BeanDefinitionHolder holder, Annotation annotation) {
        Class<? extends Annotation> type = annotation.annotationType();
        RootBeanDefinition bd = (RootBeanDefinition) holder.getBeanDefinition();

        //检查BeanMethod上的注解
        Annotation targetAnnotation = getFactoryMethodAnnotation(bd, type);
        return targetAnnotation != null && targetAnnotation.equals(annotation);
    }


    protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) {
        Method resolvedFactoryMethod = bd.getResolvedFactoryMethod();
        return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null);
    }


    //判断指定的注解是否有候选者功能，比如@Qualifier
    protected boolean isQualifier(Class<? extends Annotation> annotationType) {
        if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
            return true;
        }
        return false;
    }


    //判断指定依赖上是否有@Value注解
    @Override
    public Object getSuggestedValue(DependencyDescriptor descriptor) {
        Object value = null;
        Annotation[] annotations = descriptor.getAnnotations();
        if(annotations.length > 0){
            AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
                    AnnotatedElementUtils.forAnnotations(annotations), this.valueAnnotationType);
            if(attr != null){
                value = attr.get(AnnotationUtils.VALUE);
                if (value == null) {
                    throw new IllegalStateException("@Value注解的value属性不存在");
                }
            }
        }
        return value;
    }


    @Override
    public boolean isRequired(DependencyDescriptor descriptor) {
        //如果参数的类型是ObjectProvider，描述符的required属性为false，直接返回
        if(!descriptor.isRequired()){
            return false;
        }

        //没有@Autowired注解，说明是必须的，前提是Full模式下ConfigurationClass中的Bean方法或构造器
        //对于字段来说，肯定是要有@Autowired注解的，因此以@Autowired注解的required属性为准
        Autowired autowired = descriptor.getAnnotation(Autowired.class);
        return (autowired == null || autowired.required());
    }
}
